﻿#ifndef RTMPPUSHSTREAM_H
#define RTMPPUSHSTREAM_H

#include <QThread>
#include <atomic>
#include <QList>
#include <QMutex>
#include <QMutexLocker>


#ifdef _WIN32
//Windows
extern "C"
{
#include <libavformat/avformat.h>
#include <libavutil/mathematics.h>
#include <libavutil/time.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavformat/avformat.h>
#include <libavutil/mathematics.h>
#include <libavutil/time.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#ifdef __cplusplus
};
#endif
#endif

class RtmpPushStream : public QThread
{
    Q_OBJECT
public:
    explicit RtmpPushStream(QObject *parent = nullptr);
    ~RtmpPushStream();

    /**
     * @brief init 初始化ffmpeg 推流
     * @return
     */
    bool init();


public:
    /**
     * @brief init_encoder 初始化编码器
     * @param width
     * @param height
     * @return
     */
    AVCodecContext* init_encoder(int width, int height);

    /**
     * @brief init_rtmp_output 初始化RTMP推流
     * @param rtmp_url
     * @param codec_ctx
     * @return
     */
    AVFormatContext* init_rtmp_output(const char *rtmp_url, AVCodecContext *codec_ctx);

    /**
     * @brief update_encoder_and_stream 图像宽度和高度发生变化，需要更新编码器重新推流
     * @param image_width
     * @param image_height
     * @return
     */
    bool update_encoder_and_stream(int image_width, int image_height);

    /**
     * @brief addPacket 向推流模块输入AVPacket
     * @param packet
     */
    void addPacket(AVPacket* packet);

private:
    /**
     * @brief splitNalus 因为裸流一个AVPacket可能由多个NALU组成，因此拆NALU，查找IDR帧
     * @param data
     * @param size
     * @return idr帧的位置
     */
    int splitNalus(const uint8_t* data, int size);

    /**
     * @brief create_sei_nal_unit       创建SEI NAL单元
     * 防竞争字节（Emulation Prevention）处理
     * 检测 0x000000 ~ 0x000003 的序列，并在 0x0000{00,01,02,03} 前插入 0x03，符合 H.264 规范。
     * @param sei_text              待插入SEI的内容
     * @param sei_text_size         待插入SEI内容的长度
     * @param sei_nalu_size         返回SEI的NALU的大小
     * @return 返回SEI的NALU的内容
     */
    uint8_t* create_sei_nal_unit(const char* sei_text, int sei_text_size, int* sei_nalu_size);

protected:
    void run() override;

signals:

private:
    int64_t start_time=0;
    int videoindex=-1;
    int frame_index=0;

    // Output AVFormatContext
    AVFormatContext *h264_ofmt_ctx = NULL;

    AVStream *out_stream = NULL;

    AVCodecContext *h264_codec_ctx = NULL;
    AVCodec *h264_codec = NULL;

    /**
     * @brief m_rtmpAddr 推流地址
     */
    QString m_rtmpAddr;

    /**
     * @brief m_image_width 图像宽度
     */
    int m_image_width;
    /**
     * @brief m_image_height 图像高度
     */
    int m_image_height;

    std::atomic<bool> m_bInitSuccess;

    std::atomic<bool> m_bThreadRun;

    QList<AVPacket*> m_packetList;
    QMutex m_packetMutex;
};

#endif // RTMPPUSHSTREAM_H
